﻿using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Utils.Weixin.Models;

namespace Utils.Weixin
{
    public class WeChatOfficial : WeChatService
    {
        public WeChatOfficial(SecretOptions options, ICacheStrategy cacheStrategy) : base(options, cacheStrategy)
        {
        }

        public string CreateAuthUrl(string redirect_uri, string state, string scope = "snsapi_base")
        {
            redirect_uri = StringHelper.UrlEncode(redirect_uri);
            return $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={options.AppId}&redirect_uri={redirect_uri}&response_type=code&scope={scope}&state={state}#wechat_redirect";
        }

        public async Task<AccessToken> Login(string code)
        {
            var jtoken = await SendAsync(HttpMethod.Get, "sns/oauth2/access_token", new
            {
                appid = options.AppId,
                secret = options.AppSecret,
                code,
                grant_type = "authorization_code"
            }, false);
            var assess = jtoken.ToObject<AccessToken>();
            cacheStrategy.AddHash("user:refresh:token", assess.openid, assess);
            cacheStrategy.Add("user:token:" + assess.openid, assess.access_token, assess.expires_in - 100);
            return jtoken.ToObject<AccessToken>();
        }


        public async Task<AccessToken> RefreshToken(string refresh_token)
        {
            var jtoken = await SendAsync(HttpMethod.Get, "sns/oauth2/refresh_token", new
            {
                appid = options.AppId,
                secret = options.AppSecret,
                refresh_token,
                grant_type = "refresh_token"
            }, false);
            var assess = jtoken.ToObject<AccessToken>();
            cacheStrategy.AddHash("user:refresh:token", assess.openid, assess);
            cacheStrategy.Add("user:token:" + assess.openid, assess.access_token, assess.expires_in - 100);
            return jtoken.ToObject<AccessToken>();
        }


        public async Task<string> GetUserAssessToken(string openid)
        {
            var access_token = cacheStrategy.Get<string>("user:token:" + openid);
            if (access_token != null)
                return access_token;
            var access = cacheStrategy.GetHash<AccessToken>("user:refresh:token", openid);
            if (access == null)
                throw Error.DefaultError("用户需重新授权");
            access = await RefreshToken(access.refresh_token);
            return access.access_token;
        }

        public async Task<string> GetTicket()
        {
            var ticket = cacheStrategy.Get<string>("ticket:" + options.AppId);
            if (ticket != null)
            {
                return ticket;
            }
            var jtoken = await SendAsync(HttpMethod.Get, "cgi-bin/ticket/getticket", new
            {
                type = "jsapi"
            });
            ticket = jtoken["ticket"].ToString();
            cacheStrategy.Add("ticket:" + options.AppId, ticket, jtoken.Value<int>("expires_in"));
            return ticket;
        }

        public Dictionary<string, object> GetJsapiConfig(string url)
        {
            var ticket = GetTicket();
            string noncestr = Guid.NewGuid().ToString("N");
            int timestamp = (int)(DateTimeOffset.Now.ToUnixTimeSeconds());
            string str = "noncestr=" + noncestr + "&jsapi_ticket=" + ticket + "&timestamp=" + timestamp + "&url=" + url;
            var signature = WeixinCryptHelper.Sha1Sign(str);
            var dict = new Dictionary<string, object>
            {
                { "appId", options.AppId },
                { "timestamp", timestamp },
                { "nonceStr", noncestr },
                { "signature", signature }
            };
            return dict;
        }

        /// <summary>
        /// 用户信息
        /// </summary>
        /// <param name="openId"></param>
        /// <returns></returns>
        public async Task<UserInfo> GetUserInfo(string openId)
        {
            var access_token = await GetUserAssessToken(openId);
            var jtoken = await SendAsync(HttpMethod.Get, "sns/userinfo", new
            {
                openid = openId,
                access_token,
                lang = "zh_CN"
            }, false);
            return jtoken.ToObject<UserInfo>();
        }

    }
}
